﻿using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JBeamLibrary.Tools;

namespace JBeamLibrary
{
    public class Config : DictionaryBase
    {
        #region Indexers

        /// <summary>Gets or sets the value of the specified config key</summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string this[string key]
        {
            get { return Get(key); }
            set { Set(key, value); }
        }

        /// <summary>Gets the element at the specified index</summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public KeyValuePair<string, string> this[int index]
        {
            get
            {
                return new KeyValuePair<string, string>(
                    Dictionary.Keys.OfType<string>().ElementAt<string>(index),
                    Dictionary.Values.OfType<string>().ElementAt<string>(index)
                );
            }
        }

        #endregion

        #region Map instance properties

        #endregion

        #region Static properties

        /// <summary>Singleton instance</summary>
        private static Config _instance;

        /// <summary>Gets the path to the user configuration file</summary>
        public static string ConfigFilePath
        {
            get { return Environment.ExpandEnvironmentVariables(ConfigurationManager.AppSettings["configFilePath"]); }
        }

        /// <summary>Gets the path to the folder where the game is installed</summary>
        public static string GamePath
        {
            get { return Map["gamePath"]; }
        }

        /// <summary>Gets the game executable name</summary>
        public static string GameExe
        {
            get { return Map["gameExe"]; }
        }

        /// <summary>Gets the fullpath to the game executable</summary>
        public static string GameExeFullPath
        {
            get { return string.Format(@"{0}\{1}", GamePath, GameExe); }
        }

        /// <summary>Gets the game configuration script name</summary>
        public static string GameConfigScript
        {
            get { return Map["gameConfigScript"]; }
        }

        /// <summary>Gets the fullpath to the game config script file</summary>
        public static string GameConfigScriptFullPath
        {
            get { return string.Format(@"{0}\{1}", GamePath, GameConfigScript); }
        }

        /// <summary>Gets the name of the user running the application</summary>
        public static string UserName
        {
            get { return Map["userName"]; }
        }

        /// <summary>Gets a value indicating whether global modifiers should be enabled or remain per part dependent</summary>
        public static bool EnableGlobalModifiers
        {
            get { return bool.Parse(Map["enableGlobalModifiers"]); }
        }

        /// <summary>Gets a value indicating whether all vehicle contents should be copied when saving to a different folder</summary>
        public static bool CopyAllVehicleContentsOnSave
        {
            get { return bool.Parse(Map["copyAllVehicleContentsOnSave"]); }
        }

        /// <summary>
        /// Gets a value indicating whether grouping properties in sections should be closed when left open;
        /// (group, breakGroup, deformGroup...)
        /// </summary>
        public static bool AutoCloseGroupingProperties
        {
            get { return bool.Parse(Map["autoCloseGroupingProperties"]); }
        }

        /// <summary>
        /// Gets a value indicating whether node selection controls should propose only the nodes from the current part
        /// or from all parts in the current vehicle
        /// </summary>
        public static bool SelectNodesFromFromCurrentPartOnly
        {
            get { return bool.Parse(Map["selectNodesFromCurrentPartOnly"]); }
        }

        #endregion

        #region Singleton

        /// <summary>Gets the key/value mapping of the user configuration file</summary>
        public static Config Map
        {
            get { return _instance ?? (_instance = new Config()); }
        }

        #endregion

        #region Map instance methods

        /// <summary>Defines the value of the specified key</summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Set(string key, string value)
        {
            if (!Dictionary.Contains(key))
                Dictionary.Add(key, value);
            else
                Dictionary[key] = value;
        }

        /// <summary>
        /// Gets the value of the given key
        /// </summary>
        /// <param name="key">The key to search for</param>
        /// <param name="defaultValue">Sets the value to this if not found</param>
        /// <returns>The value for the given key</returns>
        public string Get(string key, string defaultValue = "")
        {
            if (!Dictionary.Contains(key))
                Set(key, defaultValue);
            return (string)Dictionary[key];
        }

        #endregion

        #region Static methods

        /// <summary>Reads the user configuration file</summary>
        public static void Read()
        {
            /* Set defaults if file does not exist */
            if (!File.Exists(ConfigFilePath))
            {
                SetDefaults();
                return;
            }

            /* Load data */
            SetDefaults();
            foreach (var toks in File.ReadAllLines(ConfigFilePath).Select(line => line.Split(';')).Where(toks => toks.Length >= 2))
                Map[toks[0]] = toks[1];

            /* Validate the read values */
            ValidateValues();
        }

        /// <summary>Saves the current configuration</summary>
        public static void Save()
        {
            /* Variables */
            var sb = new StringBuilder();

            /* Save all */
            foreach (DictionaryEntry entry in Map)
                sb.AppendFormat("{0};{1}{2}", entry.Key, entry.Value, Environment.NewLine);

            /* Write to file */
            var folder = Path.GetDirectoryName(ConfigFilePath);
            if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
            File.WriteAllText(ConfigFilePath, sb.ToString());
        }

        /// <summary>Sets all values to their defaults</summary>
        private static void SetDefaults()
        {
            InitGamePath();
            InitGameExe();
            InitGameConfigScript();
            InitUserName();
            InitEnableGlobalModifiers();
            InitAutoCloseGroupingProperties();
            InitCopyAllVehicleContentsOnSave();
            InitSelectNodesFromCurrentPartOnly();
            InitApplicationLayout();
        }

        /// <summary>Initializes the game path with its default value</summary>
        /// <returns></returns>
        public static bool InitGamePath()
        {
            Map["gamePath"] = Environment.ExpandEnvironmentVariables(ConfigurationManager.AppSettings["defGamePath"]);
            if (!Directory.Exists(GamePath))
                Map["gamePath"] = Registry.GetValue(ConfigurationManager.AppSettings["defRegGamePath"], "InstallLocation", string.Empty).ToStringOrEmpty();
            return Directory.Exists(GamePath);
        }

        /// <summary>Initializes the game executable file name</summary>
        /// <returns></returns>
        public static bool InitGameExe()
        {
            Map["gameExe"] = Environment.ExpandEnvironmentVariables(ConfigurationManager.AppSettings["defGameExe"]);
            return Directory.Exists(GameExe);
        }

        /// <summary>Initializes the game configuration script file name</summary>
        /// <returns></returns>
        public static bool InitGameConfigScript()
        {
            Map["gameConfigScript"] = Environment.ExpandEnvironmentVariables(ConfigurationManager.AppSettings["defGameConfigScript"]);
            return Directory.Exists(GameConfigScript);
        }

        /// <summary>Initializes the user name with the Windows user name</summary>
        /// <returns></returns>
        public static bool InitUserName()
        {
            Map["userName"] = Environment.UserName;
            return true;
        }

        /// <summary>Initializes the default value for enabling global modifiers</summary>
        /// <returns></returns>
        public static bool InitEnableGlobalModifiers()
        {
            Map["enableGlobalModifiers"] = ConfigurationManager.AppSettings["defEnableGlobalModifiers"];
            return true;
        }

        /// <summary>Initializes the default closing of grouping properties</summary>
        /// <returns></returns>
        public static bool InitAutoCloseGroupingProperties()
        {
            Map["autoCloseGroupingProperties"] = ConfigurationManager.AppSettings["defAutoCloseGroupingProperties"];
            return true;
        }

        public static bool InitCopyAllVehicleContentsOnSave()
        {
            Map["copyAllVehicleContentsOnSave"] = ConfigurationManager.AppSettings["defCopyAllVehicleContentsOnSave"];
            return true;
        }

        public static bool InitSelectNodesFromCurrentPartOnly()
        {
            Map["selectNodesFromCurrentPartOnly"] = ConfigurationManager.AppSettings["defSelectNodesFromCurrentPartOnly"];
            return true;
        }

        public static bool InitApplicationLayout()
        {
            Map["applicationLayout"] = ConfigurationManager.AppSettings["defApplicationLayout"];
            return true;
        }

        /// <summary>Validates the current values</summary>
        private static void ValidateValues()
        {
            if (!Directory.Exists(GamePath))
                InitGamePath();
            if (!File.Exists(GameExeFullPath))
                InitGameExe();
            if (!File.Exists(GameConfigScriptFullPath))
                InitGameConfigScript();
        }

        #endregion
    }
}
